#include <stdlib.h>
#include <time.h>

#include <string>

#include <GLTools.h>
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>
#include "glUtils.h"

#include "main.h"
#include "controller.h"
#include "fireworks.h"
#include "gravity.h"
#include "flocking.h"
#include "utils.h"

bool fullScreen = false;

// Handles clicking on screen input
void mouseAction(int button, int state, int x, int y){
		
    switch (particle_type){
	case FIREWORKS:
	    fireworksMouseAction(button, state, x, y);
	    break;
	case GRAVITY:
	    gravityMouseAction(button, state, x, y);
	    break;
	case FLOCKING:
	    break;
	Default:
	    break;
    }
}

//handles keyboard input
void keyboard_handler(unsigned char key, int x, int y){
  //fprintf(stdout, "BUTTON:%d\n", key);
  
  if (particle_type == FLOCKING)
  {
	  switch(key)
    {
	case 27:
	    exit(EXIT_SUCCESS);
	    break;
	case 49:
	    base_size++;
	    glUniform1i(base_size_loc, base_size);
	    printf("Base Particle Size: %i\n", base_size);
	    break;
	case 50:
	    if (base_size > 0){
		base_size--;
	    }
	    glUniform1i(base_size_loc, base_size);
	    printf("Base Particle Size: %i\n", base_size);
	    break;
	case 51:
	    incrementDepthDivisor();
	    break;
	case 52:
	    decrementDepthDivisor();
	    break;
	case 32:
	    printControls();
	    break;
	        
	
	case 113: //q
	    closest_allowed_distance +=  closest_allowed_distance_speed;
	    printf("Attempted Distance From Others: %4.2f\n", closest_allowed_distance);
	    break;
		
	case 97: //a
	    closest_allowed_distance -= closest_allowed_distance_speed;
	    printf("Attempted Distance From Others: %4.2f\n", closest_allowed_distance);
	    break;
	    

	case 119: //w
		rule1_weight += rule_speed;
		printf("Positional Spacing Effort: %4.2f\n", rule1_weight);
		break;
	case 115: //s
		rule1_weight -= rule_speed;
		printf("Positional Spacing Effort: %4.2f\n", rule1_weight);
		break;


	case 101: //e
		rule2_weight +=  rule_speed;
		printf("Flock Direction Effort: %4.2f\n", rule2_weight);
		break;
		
	case 100: //d
		rule2_weight -= rule_speed;
		printf("Flock Direction Effort: %4.2f\n", rule2_weight);
		break;
		
		
	case 114: //r
		rule3_weight +=  rule_speed;
		printf("Positional Goal Effort: %4.2f\n", rule3_weight);
		break;
		
	case 102: //f
		rule3_weight -= rule_speed;
		printf("Positional Goal Effort: %4.2f\n", rule3_weight);
		break;
		
	
	case 116: //t
		closest_allowed_colour += closest_allowed_colour_speed;
		if (closest_allowed_colour > 1.0f){
		    closest_allowed_colour = 1.0f;
		}
		printf("Attempted Color Distance From Others: %4.4f\n", closest_allowed_colour);
		break;
		
	case 103: //g
		closest_allowed_colour -=  closest_allowed_colour_speed;
		if (closest_allowed_colour < 0.0f){
		    closest_allowed_colour = 0.0f;
		}
		printf("Attempted Color Distance From Others: %4.4f\n", closest_allowed_colour);
		break;
	
		
	case 121: //y
		colour_rule1_weight +=  colour_rule_speed;
		printf("Color Differentiation Effort: %4.2f\n", colour_rule1_weight);
		break;
		
	case 104: //h
		colour_rule1_weight -=  colour_rule_speed;
		printf("Color Differentiation Effort: %4.2f\n", colour_rule1_weight);
		break;
		
		
	case 117: //u
		colour_rule2_weight +=  colour_rule_speed;
		printf("Color Direction Effort: %4.2f\n", colour_rule2_weight);
		break;
		
	case 106: //j
		colour_rule2_weight -=  colour_rule_speed;
		printf("Color Direction Effort: %4.2f\n", colour_rule2_weight);
		break;
		
		
	case 105: //i
		colour_rule3_weight +=  colour_rule_speed;
		printf("Color Goal Effort: %4.2f\n", colour_rule3_weight);
		break;
		
	case 107: //k
		colour_rule3_weight -=  colour_rule_speed;
		printf("Color Goal Effort: %4.2f\n", colour_rule3_weight);
		break;
	    
	default:
	    break;
    }
  }
  else if (particle_type == FIREWORKS)
  {
	switch(key)
    {
	case 27:
	    exit(EXIT_SUCCESS);
	    break;
	case 49:
	    base_size++;
	    glUniform1i(base_size_loc, base_size);
	    printf("Base Particle Size: %i\n", base_size);
	    break;
	case 50:
	    if (base_size > 0){
		base_size--;
	    }
	    glUniform1i(base_size_loc, base_size);
	    printf("Base Particle Size: %i\n", base_size);
	    break;
	case 51:
	    incrementDepthDivisor();
	    break;
	case 52:
	    decrementDepthDivisor();
	    break;
	case 32:
	    printControls();
	    break;
	        
	    
	case 113: //q

		MAX_INITIAL_FIREWORK_SPEED += FIREWORK_SPEED_SPEED;
		FIREWORK_VELOCITY_BIAS[1] = MAX_INITIAL_FIREWORK_SPEED;
		printf("Firework Y speed: %4.2f\n", MAX_INITIAL_FIREWORK_SPEED);
		
		break;
	case 97: //a

		MAX_INITIAL_FIREWORK_SPEED -= FIREWORK_SPEED_SPEED;
		FIREWORK_VELOCITY_BIAS[1] = MAX_INITIAL_FIREWORK_SPEED;
		printf("Firework Y speed: %4.2f\n", MAX_INITIAL_FIREWORK_SPEED);
	
		break;

	case 119: //w

		MAX_X_SPEED += FIREWORK_SPEED_SPEED;
		FIREWORK_VELOCITY_BIAS[0] = MAX_X_SPEED;
		printf("Firework X speed: %4.2f\n", MAX_X_SPEED);
		
		break;
	case 115: //s

		MAX_X_SPEED -= FIREWORK_SPEED_SPEED;
		FIREWORK_VELOCITY_BIAS[0] = MAX_X_SPEED;
		printf("Firework X speed: %4.2f\n", MAX_X_SPEED);			
		
		break;
		
		
	case 101: //e

		MAX_Z_SPEED += FIREWORK_SPEED_SPEED;
		FIREWORK_VELOCITY_BIAS[2] = MAX_Z_SPEED;
		printf("Firework Z speed: %4.2f\n", MAX_Z_SPEED);	

		break;
		
	case 100: //d

		MAX_Z_SPEED -= FIREWORK_SPEED_SPEED;
		FIREWORK_VELOCITY_BIAS[2] = MAX_Z_SPEED;
		printf("Firework Z speed: %4.2f\n", MAX_Z_SPEED);	

		break;
		
	case 114: //r

		FIREWORK_SCALE_X += FIREWORK_SPEED_SPEED;
		FIREWORK_SCALE[0] = FIREWORK_SCALE_X;
		printf("Firework X scale: %4.2f\n", FIREWORK_SCALE_X);
		break;
		
	case 102: //f
		FIREWORK_SCALE_X -= FIREWORK_SPEED_SPEED;
		FIREWORK_SCALE[0] = FIREWORK_SCALE_X;
		printf("Firework X scale: %4.2f\n", FIREWORK_SCALE_X);
		break;
		
	case 116: //t
		FIREWORK_SCALE_Y += FIREWORK_SPEED_SPEED;
		FIREWORK_SCALE[1] = FIREWORK_SCALE_Y;
		printf("Firework Y scale: %4.2f\n", FIREWORK_SCALE_Y);
		break;
		
	case 103: //g
		FIREWORK_SCALE_Y -= FIREWORK_SPEED_SPEED;
		FIREWORK_SCALE[1] = FIREWORK_SCALE_Y;
		printf("Firework Y scale: %4.2f\n", FIREWORK_SCALE_Y);
		break;
	    
	case 121: //y
		FIREWORK_SCALE_Z += FIREWORK_SPEED_SPEED;
		FIREWORK_SCALE[2] = FIREWORK_SCALE_Z;
		printf("Firework Z scale: %4.2f\n", FIREWORK_SCALE_Z);
		break;
		
	case 104: //h
		FIREWORK_SCALE_Z -= FIREWORK_SPEED_SPEED;
		FIREWORK_SCALE[2] = FIREWORK_SCALE_Z;
		printf("Firework Z scale: %4.2f\n", FIREWORK_SCALE_Z);
		break;
		
	case 117: //u
		FIREWORK_FUZZINESS +=  firework_fuzziness_increment;
		if (FIREWORK_FUZZINESS > 2.0f){
		    FIREWORK_FUZZINESS = 2.0f;
		}
		printf("Firework color fuzziness: %4.2f\n", FIREWORK_FUZZINESS);
		break;
		
	case 106: //j
		FIREWORK_FUZZINESS -=  firework_fuzziness_increment;
		if (FIREWORK_FUZZINESS < 0.0f){
		    FIREWORK_FUZZINESS = 0.0f;
		}
		printf("Firework color fuzziness: %4.2f\n", FIREWORK_FUZZINESS);
		break;
	    
	default:
	    break;
    }
  }
  else {
    switch(key)
    {
	case 27:
	    exit(EXIT_SUCCESS);
	    break;
	case 49:
	    base_size++;
	    glUniform1i(base_size_loc, base_size);
	    printf("Base Particle Size: %i\n", base_size);
	    break;
	case 50:
	    if (base_size > 0){
		base_size--;
	    }
	    glUniform1i(base_size_loc, base_size);
	    printf("Base Particle Size: %i\n", base_size);
	    break;
	case 51:
	    incrementDepthDivisor();
	    break;
	case 52:
	    decrementDepthDivisor();
	    break;
	case 32:
	    printControls();
	    break;
    }
  }
    
}

//handles special character input (up, down, left, right)
void special_handler(int key, int x, int y)
{
	switch(key)
	{
	    	case GLUT_KEY_UP:
			incrementTrailLength();
			break;
		case GLUT_KEY_DOWN:
			decrementTrailLength();
			break;
		case GLUT_KEY_RIGHT:
			incrementShader();
			break;
		case GLUT_KEY_LEFT:
			decrementShader();
			break;
		case GLUT_KEY_HOME:
		    timestep_mult += timestep_mult_step;
		    printf("System Speed: %f\n", timestep_mult);
		    break;
		case GLUT_KEY_END:
		    timestep_mult -= timestep_mult_step;
		    if (timestep_mult < 0.0f){
			timestep_mult = 0.0f;
		    }
		    printf("System Speed: %f\n", timestep_mult);
		    break;
		case GLUT_KEY_F11:
		    if (!fullScreen){
			glutFullScreen();
			printf("Full Screen Mode Enabled");
		    }
		    else{
			glutReshapeWindow(800, 600);
			printf("Full Screen Mode Disabled");
		    }
		    fullScreen = !fullScreen;
		    break;
		default:
			break;
			
	}
	
	if (particle_type == GRAVITY)
	{
		switch(key)
		{
			case GLUT_KEY_PAGE_DOWN:
				GRAVITATIONAL_CONSTANT -= GRAVITATIONAL_CONSTANT_STEP;
				printf("Gravitational Constant: %d\n", GRAVITATIONAL_CONSTANT);
				break;
				
			case GLUT_KEY_PAGE_UP:
				GRAVITATIONAL_CONSTANT += GRAVITATIONAL_CONSTANT_STEP;		
				printf("Gravitational Constant: %d\n", GRAVITATIONAL_CONSTANT);
				break;
			case GLUT_KEY_F2:
				gravity_center_toggle = !gravity_center_toggle;
				
				if (gravity_center_toggle)
					printf("Center gravity on \n");
				else 
					printf("Center gravity off\n");
				break;
			default:
			break;
		}
	}
}